package com.lkf.rpc.stub;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 客户端远程调用代理
 */
public class RPCClientProxy {

    /**
     * 根据入参（封装的ClassInfo类），创建一个代理对象，启动netty客户端，向服务端发送数据
     *
     * @param target      目标类，封装了请求调用的接口类和入参信息
     * @param packageName 目标类的接口包名，客户端调用的接口包名需与服务端监听的接口包名一致
     * @return
     */
    public static Object create(Class target, String packageName, String address, int port) {
        // 类加载器
        ClassLoader classLoader = target.getClassLoader();
        // target实质对应的是客户端与服务端相同的接口类，实现类由服务端的业务逻辑处理
        Class[] interfaces = {target};
        // 执行器
        InvocationHandler invocation = new InvocationHandler() {
            /**
             * 代理类的具体要执行方法实现
             * @param proxy 代理对象
             * @param method 代理方法
             * @param args 方法入参
             * @return 执行发发对象
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                //封装ClassInfo
                ClassInfo classInfo = new ClassInfo();
                classInfo.setClassName(target.getName());
                classInfo.setMethodName(method.getName());
                classInfo.setArgs(args);
                classInfo.setTypes(method.getParameterTypes());
                classInfo.setPackageName(packageName);

                //开始用Netty发送数据，客户端只需要一个线程组用于发送数据
                EventLoopGroup group = new NioEventLoopGroup();
                // 服务端返回接口处理类
                ResultHandler resultHandler = new ResultHandler();
                try {
                    // netty客户端启动类，配置netty参数
                    Bootstrap b = new Bootstrap();
                    // netty线程组
                    b.group(group)
                            // 配置netty的channel为NioSocketChannel
                            .channel(NioSocketChannel.class)
                            // 配置netty的的channel，使用内部类ChannelInitializer创建
                            .handler(new ChannelInitializer<SocketChannel>() {
                                /**
                                 * 初始化channel
                                 * @param socketChannel
                                 * @throws Exception
                                 */
                                @Override
                                public void initChannel(SocketChannel socketChannel) throws Exception {
                                    // 拿到channel的pipeline，用于添加自定义配置
                                    ChannelPipeline pipeline = socketChannel.pipeline();
                                    //添加编码器
                                    pipeline.addLast("encoder", new ObjectEncoder());
                                    //添加解码器  构造方法第一个参数设置二进制数据的最大字节数  第二个参数设置具体使用哪个类解析器
                                    pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                                    //添加客户端业务处理类
                                    pipeline.addLast("handler", resultHandler);
                                }
                            });
                    // 给netty绑定调用地址和端口并等待建立socket链接
                    ChannelFuture future = b.connect(address, port).sync();
                    // 将客户端封装的请求classinfo类添加到netty网络容器eventLoopGroup线程组的channel中
                    future.channel().writeAndFlush(classInfo).sync();
                    // 阻塞线程，防止程序运维完后关闭，直至socket连接关闭后才会关闭
                    future.channel().closeFuture().sync();
                } finally {
                    // 关闭线程组
                    group.shutdownGracefully();
                }
                // 返回客户端业务控制器的相应值
                return resultHandler.getResponse();
            }
        };
        //使用代理创建代理对象，并将要代理的类加载器、要代理的接口、代理执行器注入
        return Proxy.newProxyInstance(classLoader, interfaces, invocation);
    }
}
